﻿//-----------------------------------------------------------------------
// <copyright file="SimpleRenderEngine.cs" company="Flaxen Geo">
//    Copyright (c) 2011, Michael P. Gerlek.  All rights reserved.
// </copyright>
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, 
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of Flaxen Geo nor the names of its contributors may be 
//   used to endorse or promote products derived from this software without 
//   specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
// THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------

// Some of this code was derived in part from examples on the web and in the SlimDX examples,
// which had no explicit license or copyright claim.  Feel free to let me know if you want
// credit here; I (and the community) are indebted to your contributions.

namespace Flaxen.SlimDXControlLib
{
    using System;
    using SlimDX.Direct3D10;
    using SlimDX.Direct3D10_1;
    using SlimDX.DXGI;
    using Device = SlimDX.Direct3D10_1.Device1;

    /// <summary>
    /// This class is a derivation of RenderEngine which does some of the D3D management
    /// work for you.  Most users will be able to derive from this, as opposed to needing
    /// the bare RenderEngine class.
    /// Derived classes need only supply the abstract method RenderEngine.Render() to
    /// actually write into the SharedTexture object.
    /// </summary>
    public abstract class SimpleRenderEngine : RenderEngine
    {
        /// <summary>
        /// Gets or sets the target view.
        /// </summary>
        protected RenderTargetView SampleRenderView { get; set; }
        
        /// <summary>
        /// Gets or sets the depth stencil.
        /// </summary>
        protected DepthStencilView SampleDepthView { get; set; }
        
        /// <summary>
        /// Gets or sets the depth texture.
        /// </summary>
        protected Texture2D DepthTexture { get; set; }

        /// <summary>
        /// Initializes the D3D device and stuff.
        /// If you override this, be sure and call base.Initialize() at the top of your method.
        /// This is called only by the SlimDXControl.
        /// </summary>
        /// <param name="control">The SlimDXControl associated with this renderer.</param>
        public override void Initialize(SlimDXControl control)
        {
            Control = control;

            Device = new Device(DriverType.Hardware, DeviceCreationFlags.BgraSupport, FeatureLevel.Level_10_0);

            return;
        }

        /// <summary>
        /// Reinitializes the D3D stuff when needed, e.g. for window size changes.
        /// If you override this, be sure and call base.Reinitialize() at the top of your method.
        /// This is called only by the SlimDXControl.
        /// </summary>
        /// <param name="width">width of the render target, in pixels</param>
        /// <param name="height">height of the render target, in pixels</param>
        public override void Reinitialize(int width, int height)
        {
            if (width <= 0)
            {
                throw new ArgumentOutOfRangeException("width", "width must be greater than zero");
            }

            if (height <= 0)
            {
                throw new ArgumentOutOfRangeException("height", "height must be greater than zero");
            }

            WindowWidth = width;
            WindowHeight = height;

            Texture2DDescription colordesc = new Texture2DDescription();
            colordesc.BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource;
            colordesc.Format = Format.B8G8R8A8_UNorm;
            colordesc.Width = WindowWidth;
            colordesc.Height = WindowHeight;
            colordesc.MipLevels = 1;
            colordesc.SampleDescription = new SampleDescription(1, 0);
            colordesc.Usage = ResourceUsage.Default;
            colordesc.OptionFlags = ResourceOptionFlags.Shared;
            colordesc.CpuAccessFlags = CpuAccessFlags.None;
            colordesc.ArraySize = 1;

            Texture2DDescription depthdesc = new Texture2DDescription();
            depthdesc.BindFlags = BindFlags.DepthStencil;

            depthdesc.Format = Format.D32_Float_S8X24_UInt;
            depthdesc.Width = WindowWidth;
            depthdesc.Height = WindowHeight;
            depthdesc.MipLevels = 1;
            depthdesc.SampleDescription = new SampleDescription(1, 0);
            depthdesc.Usage = ResourceUsage.Default;
            depthdesc.OptionFlags = ResourceOptionFlags.None;
            depthdesc.CpuAccessFlags = CpuAccessFlags.None;
            depthdesc.ArraySize = 1;

            SharedTexture = new Texture2D(Device, colordesc);
            DepthTexture = new Texture2D(Device, depthdesc);
            SampleRenderView = new RenderTargetView(Device, SharedTexture);
            SampleDepthView = new DepthStencilView(Device, DepthTexture);

            return;
        }

        /// <summary>
        /// Override this to dispose of any managed objects in your class.
        /// Be sure to call base.DisposeManaged() at the end of your method.
        /// </summary>
        protected override void DisposeManaged()
        {
            if (SampleRenderView != null)
            {
                SampleRenderView.Dispose();
                SampleRenderView = null;
            }

            if (SampleDepthView != null)
            {
                SampleDepthView.Dispose();
                SampleDepthView = null;
            }

            if (DepthTexture != null)
            {
                DepthTexture.Dispose();
                DepthTexture = null;
            }

            if (Device != null)
            {
                Device.Dispose();
                Device = null;
            }

            base.DisposeManaged();

            return;
        }

        /// <summary>
        /// Override this to dispose of any unmanaged objects in your class.
        /// Be sure to call base.DisposeManaged() at the end of your method.
        /// </summary>
        protected override void DisposeUnmanaged()
        {
            return;
        }
    }
}
